#include <iostream>
#include <string>
#include <sstream>
#include <fstream>

/**
 * This is a letter of recommendation generator. To use this program,
 * create a generic source letter and populate the source with references
 * to {department} and {position} where applicable. {department} will be
 * expanded to the department specified, and {position} will be expanded
 * to the position the person is applying to.
 * 
 * For help, provide -h or --help as an option
 *
 * @author James Edmondson (jedmondson@gmail.com)
 **/

std::string department = "EECS at Vanderbilt University";
std::string source = "source.txt";
std::string position = "Assistant Professor";
std::string person = "James Edmondson";
std::string batch = "";

std::string target_dir = ".";
std::string filename_format = "{person} for {position} at {department}.txt";

/**
 * Reads the contents of a file into a string
 * @param filename    file to read
 * @return            contents of file
 **/
std::string read_file (const std::string filename)
{
  std::stringstream buffer;
  std::string line;

  std::ifstream myfile (filename.c_str ());
  if (myfile.is_open ())
  {
    while (myfile.good())
    {
      getline (myfile, line);
      buffer << line << std::endl;
    }
    myfile.close();
  }
  else
  {
    std::cerr << "ERROR: Unable to read " << filename << std::endl;
    exit (1);
  }

  return buffer.str ();
}

/**
 * Saves a string to a file
 * @param filename    file to write to
 * @param contents    contents of file
 **/
void save_file (const std::string filename, 
                const std::string contents)
{
  std::stringstream target_file;
  target_file << target_dir;
  target_file << "/";
  target_file << filename;

  std::cout << "  Saving letter to " << target_file.str () << "...\n";

  std::ofstream myfile (target_file.str ().c_str ());
  if (myfile.is_open ())
  {
    myfile << contents;
    myfile.close ();
  }
  else
  {
    std::cerr << "ERROR: Unable to write to " << filename << std::endl;
    exit (2);
  }
}

/**
 * Substitute the department and position variables in a buffer
 * @param   buffer      the buffer to search and replace instances in
 **/
void substitute_vars (std::string & buffer)
{  
  std::string::size_type found = buffer.find ("{department}");

  while (found != std::string::npos) 
  {
    buffer.replace (found, strlen ("{department}"), department);
    found = buffer.find ("{department}", found + department.length ());
  } 

  found = buffer.find ("{position}");

  while (found != std::string::npos) 
  {
    buffer.replace (found, strlen ("{position}"), position);
    found = buffer.find ("{position}", found + position.length ());
  } 

  found = buffer.find ("{person}");

  while (found != std::string::npos) 
  {
    buffer.replace (found, strlen ("{person}"), person);
    found = buffer.find ("{person}", found + person.length ());
  } 

}

/**
 * Splits a string of form "position,department" into global vars
 * @param position_department    string in form of position,department
 * @return                       true if correctly formatted string
 **/
bool populate_vars (std::string & position_department)
{
  std::string::size_type found = position_department.find (",");

  if (found == std::string::npos)
  {
    return false;
  }
  else
  {
    // otherwise, save the position and department
    position = position_department.substr (0, found);

    std::string::size_type count = position_department.length () - found;
    std::string::size_type begin = found + 1;

    found = position_department.find (",", begin);

    if (found != std::string::npos)
    {
      count = found - begin;
    }

    department = position_department.substr (begin, count);
  }

  return true;
}

/**
 * Processes a batch of requests. The batch_contents string should be
 * a newline separated listing of position,department.
 * @param batch_contents    list of position,department to generate
 * @param source_contents   source file to substitute batch request into
 **/
void process_batch (const std::string & batch_contents,
                    const std::string & source_contents)
{
  std::stringstream listing (batch_contents);
  std::string next;

  std::cout << "Processing batch of requests...\n";

  while (listing.good ())
  {
    getline (listing, next);
    if (populate_vars (next))
    {
      // populate the unsubstituted contents and filename
      std::string contents (source_contents);
      std::string filename (filename_format);

      // substitute the variables for person, position, department
      substitute_vars (contents);
      substitute_vars (filename);

//      std::cout << "  Saving letter to " << filename << "...\n";

      // save the new file
      save_file (filename, contents);
    }
  }
}

/**
 * Performs main program logic.
 * @param argc    number of arguments from the command line (always >= 1)
 * @param argv    arguments from the command line
 **/
int main (int argc, char ** argv)
{
  int i;
  std::string filecontents;

  for (i = 1; i < argc; ++i)
  {
    std::string arg (argv[i]);

    // user is indicating where I'm applying
    if (i + 1 < argc && 
      (arg == "--department" || arg == "-d"))
    {
      department = argv[i + 1];
    }

    // user is indicating where I'm applying
    else if (i + 1 < argc && 
      (arg == "--position" || arg == "-p"))
    {
      position = argv[i + 1];
    }

    else if (i + 1 < argc && 
      (arg == "--source" || arg == "-s"))
    {
      source = argv[i + 1];
    }

    else if (i + 1 < argc && 
      (arg == "--target-dir" || arg == "-t"))
    {
      target_dir = argv[i + 1];
    }

    else if (i + 1 < argc && 
      (arg == "--filename-format" || arg == "--fn" || arg == "-f"))
    {
      filename_format = argv[i + 1];
    }

    else if (i + 1 < argc && 
      (arg == "--batch" || arg == "-b"))
    {
      batch = argv[i + 1];
    }

    else if (i + 1 < argc && 
      (arg == "--person" || arg == "-e"))
    {
      person = argv[i + 1];
    }

    else if (arg == "--help" || arg == "-h")
    {
      std::cout << "Usage info for " << argv[0] << "\n\n";
      std::cout << "  -h or --help       : Print usage information\n";
      std::cout << "  -d or --department : Place to target the application to\n";
      std::cout << "                       Default: " << department << "\n\n";
      std::cout << "  -p or --position   : Position being applied for\n";
      std::cout << "                       Default: " << position << "\n\n";
      std::cout << "  -e or --person     : Person who is applying\n";
      std::cout << "                       Default: " << person << "\n\n";
      std::cout << "  -s or --source     : Source file for letter of recommendation\n";
      std::cout << "                       Default: " << source << "\n\n";
      std::cout << "  -b or --batch      : Generate a batch of requests from source\n";
      std::cout << "                       This will point to a file that has pairings\n";
      std::cout << "                       of Position,Dept/Instition per line, e.g.,\n\n";
      std::cout << "                       Assistant Professor, CS Department at Boston University\n";
      std::cout << "                       Associate Professor, EE Department at Harvard University\n\n";
      std::cout << "  -t or --target-dir : Target directory to save resulting letter into\n";
      std::cout << "                       Default: " << target_dir << "\n\n";
      std::cout << "  -f or --fn or      : Filename format to save to in the target directory.\n";
      std::cout << "  --filename-format    Default: " << filename_format << "\n\n";
      return 1;
    }

  }

  std::string contents = read_file (source);
  std::string batch_contents = "{position},{department}";

  if (batch != "")
    batch_contents = read_file (batch);
  else
    substitute_vars(batch_contents);


  process_batch (batch_contents, contents);

  return 0;
}
